/*
 * @Description: 
 * @version: 
 * @Author: Eve
 * @Date: 2019-07-23 15:23:34
 * @LastEditors: Eve
 * @LastEditTime: 2019-10-10 04:34:41
 */
/* 
事件侦听兼容IE
*/
const EventUtil = {
	addHandler: function(element, type, handler) {
		if (element.addEventListener) {
			element.addEventListener(type, handler, false)
		} else if (element.attachEvent) {
			element.attachEvent('on' + type, handler)
		} else {
			element['on' + type] = handler
		}
	},
	removeHandler: function(element, type, handler) {
		if (element.removeEventListener()) {
			element.removeEventListener(type, handler, false)
		} else if (element.detachEvent) {
			element.detachEvent('on' + type, handler)
		} else {
			element['on' + type] = null
		}
	}
}

/* 
判断数据类型
*/

const isType = type => target => `[object ${type}]` === Object.prototype.toString.call(target)

/* 
循环实现数组map方法
*/

const selfMap = function(fn, context) {
	let arr = Array.prototype.slice.call(this)
	// let mappedArr = Array(arr.length - 1) 原文是这么写 不明白 -1 的用意
	let mappedArr = Array(arr.length)
	for (let i = arr.length; i--; ) {
		// 判断稀疏数组的情况
		if (!arr.hasOwnProperty(i)) continue
		mappedArr[i] = fn.call(context, arr[i], i, this)
	}
	return mappedArr
}

Array.prototype.selfMap = selfMap

let a = [1, 2, 3, 4].selfMap(items => items + 1)

/* 
使用 reduce 实现数组 map 方法
*/

const redMap = function(fn, context) {
	let arr = Array.prototype.slice.call(this)
	return arr.reduce((pre, cur, index) => {
		return [...pre, fn.call(context, cur, index, this)]
	}, [])
}

/* 
循环实现数组 filter 方法
*/
const selfFilter = function(fn, context) {
	let arr = Array.prototype.slice.call(this)
	let filteredArr = []
	for (let i = 0; i < arr.length; i++) {
		if (!arr.hasOwnProperty(i)) continue
		fn.call(context, arr[i], i) && filteredArr.push(arr[i])
	}
	return filteredArr
}

/* 
使用reduce实现数组filter方法
*/

const redFilter = function(fn, context) {
	return this.reduce((pre, cur, index) => {
		return fn.call(context, cur, index, this) ? [...pre, cur] : [...pre]
	}, [])
}

/* 
使用循环实现数组some方法
*/

const selfSome = function(fn, context) {
	let res = false
	let arr = Array.prototype.slice.call(this)
	if (!arr.length) return res
	// 这里因为只返回布尔值 所以采用性能高的遍历方式
	for (let i = arr.length; i--; ) {
		if (!arr.hasOwnProperty(i)) continue
		if (fn.call(context, arr[i], i, this)) {
			return !res
		}
	}
	return res
}

/* 
循环实现数组的 reduce 方法
*/

const selfReduce = function(fn, initValue) {
	let arr = Array.prototype.slice.call(this)
	let res, startIndex
	if (initValue === undefined) {
		// 这里之所以采用循环取值 是为了避免直接对稀疏数组取值 发生不必要的错误
		for (let i = 0; i < arr.length; i++) {
			if (!arr.hasOwnProperty(i)) continue
			startIndex = i
			res = arr[i]
			break
		}
	} else {
		res = initValue
	}

	// 如果有初始值 则使用初始值 否则取第一项
	for (let i = initValue === undefined ? 0 : 1; i < arr.length; i++) {
		if (!arr.hasOwnProperty(i)) continue
		res = fn.call(null, res, arr[i], i, this)
		// 这里之所以继续使用call调用 猜测是为了不污染函数内部this
	}
	return res
}

/* 
使用reduce实现flat方法
*/

const selfFlat = function(depth = 1) {
	let arr = Array.prototype.slice.call(this)
	if (depth === 0) return arr
	return arr.reduce((pre, cur) => {
		if (Array.isArray(cur)) {
			return [...pre, ...selfFlat.call(cur, depth - 1)]
		} else {
			return [...pre, cur]
		}
	}, [])
}

/* 
dom事件添加兼容
*/
// const _on = (function() {
// 	if (document.addEventListener) {
// 		return function(element, event, handler) {
// 			if (element && event && handler) {
// 				element.addEventListener(event, handler, false)
// 			}
// 		}
// 	} else {
// 		return function(element, event, handler) {
// 			if (element && event && handler) {
// 				element.attachEvent('on' + event, handler)
// 			}
// 		}
// 	}
// })()

/* 
手动实现ES6 class
*/

function inherit(subType, superType) {
	subType.prototype = Object.create(superType.prototype, {
		constructor: {
			enumerable: false,
			configurable: true,
			writable: true,
			value: subType
		}
	})
	Object.setPrototypeOf(subType, superType)
}

/* 
函数柯里化  
固定参数个数版本
*/
/* const curry = fn => {
	let argslen = fn.length
	if (argslen < 1) return fn
	const generator = (...args) => {
		if (argslen === args.length) {
			// 这是原执行
			return fn(...args)
		} else {
			// 这里就递归 逐步拆分成单个
			return (...args2) => generator(...args, ...args2)
		}
	}
	return generator
} */

/* 
更为优秀的答案
const curry = (f, args1 = []) => (...args2) => {
  const args = [ ...args1, ...args2 ]
  return f.length === args.length
  ? f(...args)
  : curry(f, args)
}
*/

/* 
不定参数处理
例
      const add=(...args)=>{
            let vars=[]
            const curried=(...c)=>{
                vars=[...vars,...c]
            // 这里是传参所用  下面才是实际调用
                return curried
            }
            curried.toString=()=>{
              // 这里利用的是 每个函数运行完之后 会自动调用toString方法来进行实际运算
                 return vars.reduce((a,b)=>a+b,0)
            }
            // 这里就返回了 函数本身 支持无限调用
            return curried(...args)
        }


*/

/* 
不定参 柯里化
*/
const curry = (fn, args = []) => {
	let _args = [...args]
	const curried = (...rest) => {
		_args = [..._args, ...rest]
		return curried
	}
	curried.toString = function() {
		return fn(..._args)
	}
	return curried(...args)
}
let addc = curry(function() {
	let res = Array.prototype.slice.call(arguments).reduce((pre, cur) => pre + cur)
	return res
})

console.log(addc(1, 2)(1)(1, 9) + 1)

// bind实现
// 思路：类似call，但返回的是函数
Function.prototype.mybind = function(context) {
	if (typeof this !== 'function') {
		throw new TypeError('Error')
	}
	let _this = this
	let arg = [...arguments].slice(1)
	console.log(arg)
	return function F() {
		// 处理函数使用new的情况
		if (this instanceof F) {
			return new _this(...arg, ...arguments)
		} else {
			return _this.apply(context, arg.concat(...arguments))
		}
	}
}
let af = function() {
	console.log(this)
}
af.mybind({ a: 111 })
